<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <title>ClassBuilder Test</title>
  <script type="text/javascript" src="../../../../../jslib/jsunit/app/jsUnitCore.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/Base.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/RuntimeException.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/Class.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/ClassBuilder.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/ClassDefinition.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/ClassDefinitionException.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/Extends.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/IllegalArgumentException.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/Import.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/Method.js"></script>
  <script type="text/javascript" src="../../../../main/javascript/js/lang/Package.js"></script>
  <!--<script type="text/javascript" src="../../../../../target/js-lang-debug.js"></script>-->
  <!--<script type="text/javascript" src="../../../../../target/js-lang-obs.js"></script>-->
  <!--<script type="text/javascript" src="../../../../../target/js-lang.js"></script>-->
  <script type="text/javascript" src="../../TestRunner.js"></script>
  <script type="text/javascript">

    var constructor_WasCalled = false;
    var staticConstructor_WasCalled = false;

    $package('com.test.package',
        $class('TestClass', {

          $static: {
            staticVar: 1,
            otherStaticVar: null,

            $TestClass: function(){
              staticConstructor_WasCalled = true;
              this.otherStaticVar = 10;
            }
          },

          someVar: 1,

          $TestClass: function () {
            constructor_WasCalled = true;
          },

          someFunction: function() {

          }
        })
    );    

    $package('com.test.package',
        $class('TestClass2', $extends('TestClass'), {
          
          testClassInstance: null,

          $TestClass2: function() {
            this.testClassInstance = new TestClass();
          },

          otherFunction: function() {
            this.testClassInstance = new TestClass();
          }
        })
    );

    $package('com.test.package2',
        $import('com.test.package.TestClass'),
        $import('com.test.package.TestClass2'),
        $class('TestClass3', {

          testClassInstance: null,
          testClass2Instance: null,

          $TestClass3: function() {
            this.testClassInstance = new TestClass();
            this.testClass2Instance = new TestClass2();
          }
        })
    );

    $package('com.test.package2',
        $import('com.test.package.TestClass'),
        $import('com.test.package.TestClass2'),
        $class('TestClass4', $extends('TestClass3'), {

        })
    );

    $package('com.test.package2',
        $abstract_class('AbstractTestClass', {

          $abstract: {
            someAbstractMethod: function() {},

            someOtherAbstractMethod: function() {}
          }
        })
     );

    var somePkg = {};

    somePkg.RegularClass = function() {
      this.message = 'This is just a regular function';
    };

    somePkg.RegularClass.prototype.getMessage = function() {
      return this.message;
    };

    function testClass() {
      assertNotNull(com.test.package.TestClass);
      var instance = new com.test.package.TestClass();

      assertTrue(constructor_WasCalled);
      assertUndefined(com.test.package.TestClass.someFunction);
      assertNotUndefined(instance.someFunction);
      assertUndefined(com.test.package.TestClass.someVar);
      assertNotUndefined(instance.someVar);

      assertTrue(staticConstructor_WasCalled);
      assertNotNull(com.test.package.TestClass.otherStaticVar);
      assertNotNull(com.test.package.TestClass.staticVar);
    }

    function testClass_WithInheritance() {
      assertNotNull(com.test.package.TestClass2);
      var instance = new com.test.package.TestClass2();
      instance.otherFunction();

      assertUndefined(com.test.package.TestClass2.someFunction);
      assertNotUndefined(instance.someFunction);
      assertUndefined(com.test.package.TestClass2.someVar);
      assertNotUndefined(instance.someVar);
      assertNotUndefined(instance.testClassInstance);
      assertEquals('com.test.package.TestClass', instance.testClassInstance.getClass().getCanonicalClassName());

      assertNotNull(com.test.package.TestClass2.otherStaticVar);
      assertNotNull(com.test.package.TestClass2.staticVar);
      assertUndefined(window.TestClass);
    }

    function testClass_WithInheritance_WithRegularBaseClass() {
      $package('test',
          $class('TestClassFromRegularClass', $extends(somePkg.RegularClass), {


          })
      );

      assertNotNull(test.TestClassFromRegularClass);
      var instance = new test.TestClassFromRegularClass();

      assertUndefined(test.TestClassFromRegularClass.getMessage);
      assertNotUndefined(instance.getMessage);
      assertEquals('This is just a regular function', instance.getMessage());


      $package('test',
          $import('somePkg.RegularClass'),
          $class('TestClassFromRegularClass2', $extends('RegularClass'), {


          })
      );

      assertNotNull(test.TestClassFromRegularClass2);
      var instance2 = new test.TestClassFromRegularClass2();

      assertUndefined(test.TestClassFromRegularClass2.getMessage);
      assertNotUndefined(instance2.getMessage);
      assertEquals('This is just a regular function', instance2.getMessage());

      $package('test',
          $class('TestClassFromRegularClass3', $extends('somePkg.RegularClass'), {


          })
      );

      assertNotNull(test.TestClassFromRegularClass3);
      var instance3 = new test.TestClassFromRegularClass3();

      assertUndefined(test.TestClassFromRegularClass3.getMessage);
      assertNotUndefined(instance3.getMessage);
      assertEquals('This is just a regular function', instance3.getMessage());
    }

    function testClass_WithImports() {
      assertNotNull(com.test.package2.TestClass3);
      var instance = new com.test.package2.TestClass3();

      assertNotUndefined(instance.testClassInstance);
      assertEquals('com.test.package.TestClass', instance.testClassInstance.getClass().getCanonicalClassName());
      assertNotUndefined(instance.testClass2Instance);
      assertEquals('com.test.package.TestClass2', instance.testClass2Instance.getClass().getCanonicalClassName());
    }

    function testClass_WithImportsAndNoCostructor() {
      assertNotNull(com.test.package2.TestClass4);
      var instance = new com.test.package2.TestClass4();

      assertNotUndefined(instance.testClassInstance);
      assertEquals('com.test.package.TestClass', instance.testClassInstance.getClass().getCanonicalClassName());
      assertNotUndefined(instance.testClass2Instance);
      assertEquals('com.test.package.TestClass2', instance.testClass2Instance.getClass().getCanonicalClassName());
    }

    function testClass_WithImportsAndStaticBlock() {
      $package('test',
        $import('com.test.package.TestClass'),

        $class('TestClassWithStaticBlock', {
          $static: {
            $TestClassWithStaticBlock: function() {
              new TestClass();  
            },

            someStaticMethod: function() {
              new TestClass();
            }
          }
        })
      );

      assertNotNull(test.TestClassWithStaticBlock);
      assertNotUndefined(test.TestClassWithStaticBlock.someStaticMethod);
      test.TestClassWithStaticBlock.someStaticMethod();      
    }

    function testAbstractClass() {
      assertNotNull(com.test.package2.AbstractTestClass);

      assertTrue(com.test.package2.AbstractTestClass.$class.isAbstract());
      assertArrayEquals(['someAbstractMethod', 'someOtherAbstractMethod'], com.test.package2.AbstractTestClass.$class.getAbstractMethods());

      try {
        $package('test',
            $abstract_class('FaultyAbstractClass', {
              $abstract: {
                testMethod: function() {
                  alert('this is not valid!!');
                }
              }
            })
        );
        fail('Should have gotten an exception because an abstract method cannot have an implementation');
      } catch (e if e instanceof js.lang.ClassDefinitionException) {
        assertUndefined(window.FaultAbstractClass);
        assertEquals('Member testMethod cannot have an implementation as it is declared in the abstract block of the class', e.getMessage());
      }

      try {
        $package('test',
            $abstract_class('FaultyAbstractClass2', {
              $abstract: {
                someProperty: 'someValue'
              }
            })
        );
        fail('Should have gotten an exception because an abstract block cannot have a property');
      } catch (e if e instanceof js.lang.ClassDefinitionException) {
        assertUndefined(window.FaultyAbstractClass2);
        assertEquals('Member someProperty is not a function. Only functions can be declared in the abstract block of a class',
            e.getMessage());
      }

      try {
        $package('test',
            $import('com.test.package2.AbstractTestClass'),
            $class('FaultyConcreteTestClass', $extends('AbstractTestClass'), {

              someOtherAbstractMethod: function() {
                alert('blah');
              }
            })
        );
        fail('Should have gotten an exception because the abstract methods are not overriden');
      } catch (e if e instanceof js.lang.ClassDefinitionException) {
        assertUndefined(window.FaultyConcreteTestClass);
        assertEquals('Abstract method(s) someAbstractMethod must be implemented', e.getMessage());
      }

      try {
        $package('test',
            $import('com.test.package2.AbstractTestClass'),
            $class('AnotherAbstractClass', {

              $abstract: {
                someAbstractMethod: function() { }
              }
            })
        );
        fail('Should have gotten and exception because the class should be declared using $abstract_class()');
      } catch (e if e instanceof js.lang.ClassDefinitionException) {
        assertUndefined(window.AnotherAbstractClass);
        assertEquals('This class has a $abstract block so it should be declared as $abstract_class', e.getMessage());
      }

      try {
        $package('test',
            $import('com.test.package2.AbstractTestClass'),
            $abstract_class('SemiAbstractClass', $extends('AbstractTestClass'), {

              someAbstractMethod: function() {
                alert('some implementation')
              }

            })
        );

        $package('test',
            $class('FaultyConcreteClass2', $extends('SemiAbstractClass'), {

            })
        );
        fail('Should have gotten an exception because only one abstract method is overriden');
      } catch (e if e instanceof js.lang.ClassDefinitionException) {
        assertUndefined(window.FaultyConcreteClass2);
        assertEquals('Abstract method(s) someOtherAbstractMethod must be implemented', e.getMessage());
      }
    }

  </script>
</head>
<body onload="new TestRunner().runTests()">
</body>
</html>